package com.gitee.luosl.cola.lang;

import com.gitee.luosl.cola.lang.function.Consumer3;
import com.gitee.luosl.cola.lang.function.Function3;
import com.gitee.luosl.cola.lang.function.Predicate3;

import java.util.Objects;
import java.util.function.Function;

public final class Tuple3<T1, T2, T3> implements Tuple {

    public final T1 _1;

    public final T2 _2;

    public final T3 _3;

    Tuple3(T1 _1, T2 _2, T3 _3) {
        this._1 = _1;
        this._2 = _2;
        this._3 = _3;
    }

    public T1 get_1() {
        return _1;
    }

    public T2 get_2() {
        return _2;
    }

    public T3 get_3() {
        return _3;
    }

    public <R> Tuple3<R, T2, T3> map1(Function<? super T1, ? extends R> function){
        return Tuple.of(function.apply(_1), _2, _3);
    }

    public <R> Tuple3<T1, R, T3> map2(Function<? super T2, ? extends R> function){
        return Tuple.of(_1, function.apply(_2), _3);
    }

    public <R> Tuple3<T1, T2, R> map3(Function<? super T3, ? extends R> function){
        return Tuple.of(_1, _2, function.apply(_3));
    }

    public <R1, R2, R3> Tuple3<R1, R2, R3> map(Function3<? super T1, ? super T2, ? super T3, Tuple3<R1, R2, R3>> mapper){
        return apply(mapper);
    }

    public <R> R apply(Function3<? super T1, ? super T2, ? super T3, ? extends R> mapper){
        return mapper.apply(_1, _2, _3);
    }

    public void accept(Consumer3<? super T1, ? super T2, ? super T3> consumer){
        consumer.accept(_1, _2, _3);
    }

    public boolean test(Predicate3<? super T1, ? super T2, ? super T3> predicate){
        return predicate.test(_1, _2, _3);
    }

    public Tuple3<T3, T2, T1> reverse() {
        return Tuple.of(_3, _2, _1);
    }


    @Override
    public int hashCode() {
        int result = 1;
        result = 31 * result + Objects.hashCode(_1);
        result = 31 * result + Objects.hashCode(_2);
        result = 31 * result + Objects.hashCode(_3);
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        } else if (!(obj instanceof Tuple3)) {
            return false;
        } else {
            final Tuple3<?, ?, ?> that = (Tuple3<?, ?, ?>) obj;
            return Objects.equals(this._1, that._1)
                    && Objects.equals(this._2, that._2)
                    && Objects.equals(this._3, that._3);
        }
    }

    @Override
    public String toString() {
        return String.format(
                "(%s, %s, %s)",
                Objects.toString(_1),
                Objects.toString(_2),
                Objects.toString(_3)
        );
    }
}
